library(spatialLIBD)
library(scuttle)
library(dplyr)
library(ggcorrplot)
library(ggplot2)
library(EBImage)
library(ggpubr)
library(foreach)
library(doParallel)
registerDoParallel(8)

Get Histology Image and Plot

# get histology image
img <- SpatialExperiment::imgRaster(spe_sub)

## Transform to a rasterGrob object
grob <- grid::rasterGrob(img, width = grid::unit(1, "npc"), height = grid::unit(1, "npc"))

## Make a plot using geom_spatial
p <- ggplot2::ggplot(
    sample_df,
    ggplot2::aes(
        x = pxl_col_in_fullres * SpatialExperiment::scaleFactors(spe_sub),
        y = pxl_row_in_fullres * SpatialExperiment::scaleFactors(spe_sub),
    )
) +
    geom_spatial(
        data = tibble::tibble(grob = list(grob)),
        ggplot2::aes(grob = grob),
        x = 0.5,
        y = 0.5
    )

## Show the plot
print(p)

png_link <- imgData(spe)[imgData(spe)$sample_id == sample_id,]$data[1]
img <- readImage('https://spatial-dlpfc.s3.us-east-2.amazonaws.com/images/151507_tissue_lowres_image.png')
#display(img)
red <- imageData(EBImage::channel(img, 'red'))
green <- imageData(EBImage::channel(img, 'green'))
blue <- imageData(EBImage::channel(img, 'blue'))
grey <- imageData(EBImage::channel(img, 'grey'))
hist(red)

hist(green)

hist(blue)

hist(grey)

display(red)
display(green)
display(blue)
# create boolean array for logcounts
logcounts_151507 <- logcounts(spe_sub)

# transpose logcounts matrix
logcounts_151507 <- t(logcounts_151507)

# remove columns with 0 max gene expression vals across all barcodes
zv <- apply(logcounts_151507, 2, function(x) length(unique(x)) == 1)
logcounts_151507_filtered <- as.matrix(logcounts_151507[,!zv])

dim(logcounts_151507)
[1]  4165 33538
dim(logcounts_151507_filtered)
[1]  4165 21134

Create DF Holding Pixel locations and Pixel Color Channel Values

# get scale factor
scaling <- SpatialExperiment::scaleFactors(spe_sub)

# get names of barcodes in sample, filter pxl dataframe to only include those barcodes
spatial_coords <- spatialCoords(spe_sub)

# create data frame
data <- data.frame(spatial_coords, row.names=NULL)
data['barcode'] <- rownames(spatial_coords)

# add scaled pixel columns
new <- ceiling(data[["pxl_col_in_fullres"]] * scaling)
data[ , ncol(data) + 1] <- new 
colnames(data)[ncol(data)] <- "scaled_pxl_col_in_lowres"

# add scaled pixel rows
new <- ceiling(data[["pxl_row_in_fullres"]] * scaling)
data[ , ncol(data) + 1] <- new 
colnames(data)[ncol(data)] <- "scaled_pxl_row_in_lowres"
# create vectors to be added to dataframe
X_r <- vector(mode="double", length = dim(data)[1])
X_g <- vector(mode="double", length = dim(data)[1])
X_b <- vector(mode="double", length = dim(data)[1])
X_grey <- vector(mode="double", length = dim(data)[1])

# iterate over dataframe, add rgb values
for (i in 1:nrow(data)) {
    col_index <- data[i,]$scaled_pxl_col_in_lowres
    row_index <- data[i,]$scaled_pxl_row_in_lowres
    X_r[i] <- red[col_index, row_index]
    X_g[i] <- green[col_index, row_index]
    X_b[i] <- blue[col_index, row_index]
    X_grey[i] <- grey[col_index, row_index]
}

data[ , ncol(data) + 1] <- X_r
colnames(data)[ncol(data)] <- "red"
data[ , ncol(data) + 1] <- X_g
colnames(data)[ncol(data)] <- "green"
data[ , ncol(data) + 1] <- X_b
colnames(data)[ncol(data)] <- "blue"
data[ , ncol(data) + 1] <- X_grey
colnames(data)[ncol(data)] <- "grey"
dim(data)
[1] 4165    9
data

Create ind_barcodes DF only if each spot has multiple pixels associated so that mean/median/modecan be computed (For HighRes Images)

# Mode Function
getmode <- function(v) {
   uniqv <- unique(v)
   uniqv[which.max(tabulate(match(v, uniqv)))]
}

# 
populate_ind_barcodes <- function(i, ind_barcodes) {
  colors <- c("red", "green", "blue", "grey")
  for (j in 1:length(colors)) {
    color <- colors[j]
    vals <- data[data$barcode == row.names(ind_barcodes)[i],][,color]
    ind_barcodes[i, paste("mean_", color, sep="")] <- mean(vals)
    ind_barcodes[i, paste("median_", color, sep="")] <- median(vals)
    ind_barcodes[i, paste("mode_", color, sep="")] <- getmode(vals) 
  }
  ind_barcodes
}
# create empty df
ind_barcodes <- data.frame(matrix(ncol = 12, nrow = length(unique(data[,'barcode']))))
colnames(ind_barcodes) <- c('mean_red', 'median_red', 'mode_red', 'mean_green', 'median_green', 'mode_green', 'mean_blue', 'median_blue', 'mode_blue', 'mean_grey', 'median_grey', 'mode_grey')
rownames(ind_barcodes) <- unique(data[, 'barcode'])

# add mean, median, mode data
for (i in 1:nrow(ind_barcodes)) {
  ind_barcodes <- populate_ind_barcodes(i, ind_barcodes)
}
ind_barcodes

Plot Histogram of Color Channel Values at a given barcode

# histogram of color channel values in one spot
barcode = rownames(ind_barcodes)[1]
x <- data[data$barcode == barcode,'green']
hist(x, main="Histogram of Green Color Channel Values For a Single Barcode",breaks=100)

Function to Compute Corr Matrix in Parallel

compute_corr_matrix <- function(expression_arr, color_arr) {
  foreach(i = 1:ncol(expression_arr),
  .combine = rbind,
  .packages = c('data.table', 'doParallel')) %dopar% {
    colName <- colnames(expression_arr)[i]
    df <- data.frame(round(cor(expression_arr[,i], color_arr, method = 'pearson', use="complete.obs"), 3))
    rownames(df) <- colName
    df
  }
}

Analysis 1: Compute Correlation First, Compute Outliers After

In the following blocks, the correlation of between all genes + pixel color channel values is calculated. Then, for the top performing genes, a plotting function is called in which spots corresponding to outlier gene expression values + spots corresponding to outlier pixel color channel values are removed.

# uncomment this line if each spot has one pixel
color_arr <- data[,6:ncol(data)]

# uncomment this line if each spot has multiple pixels
# color_arr <- ind_barcodes
correlation_matrix <- compute_corr_matrix(logcounts_151507_filtered, color_arr)
# set threshold and filter
threshold <- 0.3
correlation_matrix_filter <- correlation_matrix
filter <- as.data.frame(apply(abs(correlation_matrix_filter) >= threshold, 1, any))
correlation_matrix_filter <- correlation_matrix_filter[filter[,1],]

# rename rownames based on gene name instead of gene ID
for (i in 1:nrow(correlation_matrix_filter)) {
  g_name <- rownames(correlation_matrix_filter)[i]
  rownames(correlation_matrix_filter)[i] <- rowData(spe[g_name])$gene_name
}
correlation_matrix_filter
# plot  correlation matrix
ggcorrplot(correlation_matrix_filter, sig.level=0.01, lab_size = 4.5, p.mat = NULL,
           insig = c("pch", "blank"), pch = 1, pch.col = "black", pch.cex =1,
           tl.cex = 7) +
  theme(axis.text.x = element_text(margin=margin(-2,0,0,0)),
        axis.text.y = element_text(margin=margin(0,-2,0,0)),
        panel.grid.minor = element_line(size=7)) + 
  geom_tile(fill="white") +
  geom_tile(height=1, width=1)

# function to calculate regression equation. Used as a sanity check
lm_eqn <- function(df){
    m <- lm(mean_red ~ MTRNR2L8, df);
    eq <- substitute(italic(y) == a + b %.% italic(x)*","~~italic(r)^2~"="~r2, 
         list(a = format(unname(coef(m)[1]), digits = 2),
              b = format(unname(coef(m)[2]), digits = 2),
             r2 = format(summary(m)$r.squared, digits = 3)))
    as.character(as.expression(eq));
}

# plot color channel value vs gene expression. If remove_outliers == TRUE, outlier gene expression values + outlier pixel color values are removed.
# ASSUMES THAT EACH BARCODE HAS MULTIPLE PIXELS
plot_channel_highres <- function(g_name, gene_id, color, stat, log_arr, color_arr, remove_outliers) {
  stat_type <- paste(color, stat, sep="_")
  x_axis <- log_arr[,gene_id]
  y_axis <- color_arr[,stat_type] * 255
  df <- data.frame(x_axis, y_axis)
  if (remove_outliers) {
    barcode_names <- rownames(log_arr)
    overlap <- union(rownames(log_arr)[isOutlier(log_arr[,gene_id])], rownames(color_arr)[isOutlier(color_arr[,stat_type])])
    rownames(df) <- barcode_names
    df <- df[-which(rownames(df) %in% overlap),]
    title <- paste(stat_type, " Channel Values vs ", g_name, " Gene Expression with Outliers Removed", sep="")
  } else {
    title <- paste(stat_type, " Channel Values vs ", g_name, " Gene Expression without Outliers Removed", sep="")
  }
  ggplot(df, aes(x=df[,1], y=df[,2])) + xlab(g_name) + ylab(stat_type) + geom_point(color=color) +
  geom_smooth(method='lm', color='black') + stat_regline_equation(label.y = 190, aes(label = ..eq.label..)) +
  stat_regline_equation(label.y = 180, aes(label = ..rr.label..)) + ggtitle(title) 
}

# call plot_channel function for all color and mean/median/mode combinations
plot_all_channels <- function(g_name, log_arr, color_arr, remove_outliers) {
  gene_id <- rowData(spe)[rowData(spe)$gene_name == g_name,]$gene_id
  colors <- c('red', 'green', 'blue', 'grey')
  stats <- c('mean', 'median', 'mode')

  for (color in colors) {
    for (stat in stats) {
      print(plot_channel_highres(g_name, gene_id, color, stat, log_arr, color_arr, remove_outliers)) 
    }
  }
}

# plot color channel value vs gene expression. If remove_outliers == TRUE, outlier gene expression values + outlier pixel color values are removed.
# ASSUMES THAT EACH BARCODE HAS ONE PIXEL
plot_channel <- function(g_name, gene_id, color, log_arr, color_arr, remove_outliers) {
  x_axis <- log_arr[,gene_id]
  y_axis <- color_arr[,color] * 255
  df <- data.frame(x_axis, y_axis)
  if (remove_outliers) {
    barcode_names <- rownames(log_arr)
    overlap <- union(rownames(log_arr)[isOutlier(log_arr[,gene_id])], rownames(color_arr)[isOutlier(color_arr[,color])])
    rownames(df) <- barcode_names
    df <- df[-which(rownames(df) %in% overlap),]
    title <- paste(color, " Channel Values vs ", g_name, " Gene Expression with Outliers Removed", sep="")
  } else {
    title <- paste(color, " Channel Values vs ", g_name, " Gene Expression without Outliers Removed", sep="")
  }
  ggplot(df, aes(x=df[,1], y=df[,2])) + xlab(g_name) + ylab(color) + geom_point(color=color) +
  geom_smooth(method='lm', color='black') + stat_regline_equation(label.y = 190, aes(label = ..eq.label..)) +
  stat_regline_equation(label.y = 180, aes(label = ..rr.label..)) + ggtitle(title) 
}

# call plot_channel function for all colors
plot_all_channels <- function(g_name, log_arr, color_arr, remove_outliers) {
  gene_id <- rowData(spe)[rowData(spe)$gene_name == g_name,]$gene_id
  colors <- c('red', 'green', 'blue', 'grey')

  for (color in colors) {
    print(plot_channel(g_name, gene_id, color, log_arr, color_arr, remove_outliers))
  }
}
plot_all_channels('MT-ND1', logcounts_151507_filtered, color_arr, remove_outliers=FALSE)
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

plot_all_channels('MT-ND1', logcounts_151507_filtered, color_arr, remove_outliers=TRUE)
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

plot_all_channels('MT-CO2', logcounts_151507_filtered, color_arr, remove_outliers=FALSE)
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

plot_all_channels('MT-CO2', logcounts_151507_filtered, color_arr, remove_outliers=TRUE)
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

plot_all_channels('MT-ATP6', logcounts_151507_filtered, color_arr, remove_outliers=FALSE)
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

plot_all_channels('MT-ATP6', logcounts_151507_filtered, color_arr, remove_outliers=TRUE)
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

plot_all_channels('MT-CYB', logcounts_151507_filtered, color_arr, remove_outliers=FALSE)
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

plot_all_channels('MT-CYB', logcounts_151507_filtered, color_arr, remove_outliers=TRUE)
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'
`geom_smooth()` using formula 'y ~ x'

Redo Analysis Removing Outliers Before Correlation Computation

# Remove outlier gene expression values. If median gene expression value is 0, replace complement of outliers with NA. Else replace outliers with NA. If remaining non-NA expression values < num, replace entire column with NA.
remove_outliers <- function(arr, num) {
  foreach(i = 1:ncol(arr),
  .combine = cbind,
  .packages = c('data.table', 'doParallel')) %dopar% {
    if (median(arr[,i]) == 0) {
      arr[,i][!isOutlier(arr[,i])] <- NA
    } else {
      arr[,i][isOutlier(arr[,i])] <- NA
    }
    if (matrixStats::count(!is.na(arr[,i])) < num) {
      arr[,i] <- rep(NA, nrow(arr))
    }
    arr[,i]
  }
}
# Call remove outlier function
logcounts_temp <- as.matrix(logcounts_151507_filtered)
col_names <- colnames(logcounts_temp)
logcounts_151507_filtered_no_outliers <- remove_outliers(logcounts_temp, 100)
colnames(logcounts_151507_filtered_no_outliers) <- col_names
logcounts_151507_filtered_no_outliers
# Remove columns that are completely NA
not_all_na <- function(x) any(!is.na(x))
logcounts_151507_filtered_no_outliers <- data.frame(logcounts_151507_filtered_no_outliers) %>% select(where(not_all_na))
dim(logcounts_151507_filtered_no_outliers)
[1] 4165 9807
# compute correlation matrix
mat <- compute_corr_matrix(logcounts_151507_filtered_no_outliers, color_arr)
# filter corr matrix based on threshold
threshold <- 0.35
correlation_matrix_filter <- mat
filter <- as.data.frame(apply(abs(correlation_matrix_filter) >= threshold, 1, any))
correlation_matrix_filter <- na.omit(correlation_matrix_filter[filter[,1],])
correlation_matrix_filter
# rename rows of correlation matrix with gene name
for (i in 1:nrow(correlation_matrix_filter)) {
  g_name <- rownames(correlation_matrix_filter)[i]
  print(g_name)
  rownames(correlation_matrix_filter)[i] <- rowData(spe[g_name])$gene_name
}
[1] "ENSG00000134201"
[1] "ENSG00000158711"
[1] "ENSG00000186205"
[1] "ENSG00000205795"
[1] "ENSG00000091409"
[1] "ENSG00000187288"
[1] "ENSG00000114698"
[1] "ENSG00000163132"
[1] "ENSG00000164066"
[1] "ENSG00000106003"
[1] "ENSG00000148204"
[1] "ENSG00000134817"
[1] "ENSG00000177283"
[1] "ENSG00000025423"
[1] "ENSG00000051825"
[1] "ENSG00000140022"
[1] "ENSG00000217930"
[1] "ENSG00000141569"
[1] "ENSG00000167676"
[1] "ENSG00000183579"
[1] "ENSG00000198712"
correlation_matrix_filter
# Correlation plot visualization
ggcorrplot(correlation_matrix_filter, sig.level=0.01, lab_size = 4.5, p.mat = NULL,
           insig = c("pch", "blank"), pch = 1, pch.col = "black", pch.cex =1,
           tl.cex = 7) +
  theme(axis.text.x = element_text(margin=margin(-2,0,0,0)),
        axis.text.y = element_text(margin=margin(0,-2,0,0)),
        panel.grid.minor = element_line(size=7)) + 
  geom_tile(fill="white") +
  geom_tile(height=1, width=1)

plot_all_channels('MARC1', logcounts_151507_filtered_no_outliers, color_arr, remove_outliers=FALSE)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4049 rows containing non-finite values (stat_smooth).
Warning: Removed 4049 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4049 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4049 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4049 rows containing non-finite values (stat_smooth).
Warning: Removed 4049 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4049 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4049 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4049 rows containing non-finite values (stat_smooth).
Warning: Removed 4049 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4049 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4049 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4049 rows containing non-finite values (stat_smooth).
Warning: Removed 4049 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4049 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4049 rows containing missing values (geom_point).

plot_all_channels('HSD17B6', logcounts_151507_filtered_no_outliers, color_arr, remove_outliers=FALSE)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4042 rows containing non-finite values (stat_smooth).
Warning: Removed 4042 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4042 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4042 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4042 rows containing non-finite values (stat_smooth).
Warning: Removed 4042 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4042 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4042 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4042 rows containing non-finite values (stat_smooth).
Warning: Removed 4042 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4042 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4042 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4042 rows containing non-finite values (stat_smooth).
Warning: Removed 4042 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4042 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4042 rows containing missing values (geom_point).

plot_all_channels('MSX1', logcounts_151507_filtered_no_outliers, color_arr, remove_outliers=FALSE)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 3996 rows containing non-finite values (stat_smooth).
Warning: Removed 3996 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3996 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3996 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 3996 rows containing non-finite values (stat_smooth).
Warning: Removed 3996 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3996 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3996 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 3996 rows containing non-finite values (stat_smooth).
Warning: Removed 3996 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3996 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3996 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 3996 rows containing non-finite values (stat_smooth).
Warning: Removed 3996 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3996 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3996 rows containing missing values (geom_point).

plot_all_channels('MT-CO2', logcounts_151507_filtered_no_outliers, color_arr, remove_outliers=FALSE)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 20 rows containing non-finite values (stat_smooth).
Warning: Removed 20 rows containing non-finite values (stat_regline_equation).
Warning: Removed 20 rows containing non-finite values (stat_regline_equation).
Warning: Removed 20 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 20 rows containing non-finite values (stat_smooth).
Warning: Removed 20 rows containing non-finite values (stat_regline_equation).
Warning: Removed 20 rows containing non-finite values (stat_regline_equation).
Warning: Removed 20 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 20 rows containing non-finite values (stat_smooth).
Warning: Removed 20 rows containing non-finite values (stat_regline_equation).
Warning: Removed 20 rows containing non-finite values (stat_regline_equation).
Warning: Removed 20 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 20 rows containing non-finite values (stat_smooth).
Warning: Removed 20 rows containing non-finite values (stat_regline_equation).
Warning: Removed 20 rows containing non-finite values (stat_regline_equation).
Warning: Removed 20 rows containing missing values (geom_point).

plot_all_channels('PLIN4', logcounts_151507_filtered_no_outliers, color_arr, remove_outliers=FALSE)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 3969 rows containing non-finite values (stat_smooth).
Warning: Removed 3969 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3969 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3969 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 3969 rows containing non-finite values (stat_smooth).
Warning: Removed 3969 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3969 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3969 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 3969 rows containing non-finite values (stat_smooth).
Warning: Removed 3969 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3969 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3969 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 3969 rows containing non-finite values (stat_smooth).
Warning: Removed 3969 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3969 rows containing non-finite values (stat_regline_equation).
Warning: Removed 3969 rows containing missing values (geom_point).

plot_all_channels('CIDEC', logcounts_151507_filtered_no_outliers, color_arr, remove_outliers=FALSE)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4063 rows containing non-finite values (stat_smooth).
Warning: Removed 4063 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4063 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4063 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4063 rows containing non-finite values (stat_smooth).
Warning: Removed 4063 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4063 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4063 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4063 rows containing non-finite values (stat_smooth).
Warning: Removed 4063 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4063 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4063 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4063 rows containing non-finite values (stat_smooth).
Warning: Removed 4063 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4063 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4063 rows containing missing values (geom_point).

plot_all_channels('ELK4', logcounts_151507_filtered_no_outliers, color_arr, remove_outliers=FALSE)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4043 rows containing non-finite values (stat_smooth).
Warning: Removed 4043 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4043 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4043 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4043 rows containing non-finite values (stat_smooth).
Warning: Removed 4043 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4043 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4043 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4043 rows containing non-finite values (stat_smooth).
Warning: Removed 4043 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4043 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4043 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4043 rows containing non-finite values (stat_smooth).
Warning: Removed 4043 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4043 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4043 rows containing missing values (geom_point).

plot_all_channels('LFNG', logcounts_151507_filtered_no_outliers, color_arr, remove_outliers=FALSE)
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4059 rows containing non-finite values (stat_smooth).
Warning: Removed 4059 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4059 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4059 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4059 rows containing non-finite values (stat_smooth).
Warning: Removed 4059 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4059 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4059 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4059 rows containing non-finite values (stat_smooth).
Warning: Removed 4059 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4059 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4059 rows containing missing values (geom_point).
`geom_smooth()` using formula 'y ~ x'
Warning: Removed 4059 rows containing non-finite values (stat_smooth).
Warning: Removed 4059 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4059 rows containing non-finite values (stat_regline_equation).
Warning: Removed 4059 rows containing missing values (geom_point).

dim(logcounts_temp)
[1]  4165 21134
dim(logcounts_151507_filtered_no_outliers)
[1] 4165 9807

Find Location of Outliers on Histology Image

# plot_outliers_in_lowres <- function(g_name, stat_type) {
#   overlap <- union(rownames(max_ex_corr)[isOutlier(max_ex_corr[,g_name])], rownames(ind_barcodes)[isOutlier(ind_barcodes[,stat_type])])
#   x_axis <- data[which(data$barcode %in% overlap),]$scaled_pxl_col_in_lowres
#   y_axis <- data[which(data$barcode %in% overlap),]$scaled_pxl_row_in_lowres
#   
#   ggplot(data.frame(x_axis, y_axis), aes(x=x_axis, y=y_axis)) + xlab('Scaled Pixel Column in LowRes') + ylab('Scaled Pixel Row in LowRes') + geom_point(color='navy') +
#     ggtitle(paste('Location of',stat_type, g_name, "Outliers on LowRes Image", sep=" ")) 
# }
# ```
# 
# 
# ```{r}
# plot_outliers_in_lowres('MT3', 'mean_red')
# plot_outliers_in_lowres('MT3', 'mean_blue')
# plot_outliers_in_lowres('MT3', 'mean_green')
# ```
# 
# ```{r}
# plot_outliers_in_lowres('MTRNR2L8', 'mean_red')
# plot_outliers_in_lowres('MTRNR2L8', 'mean_blue')
# plot_outliers_in_lowres('MTRNR2L8', 'mean_green')
# ```
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CmxpYnJhcnkoc3BhdGlhbExJQkQpCmxpYnJhcnkoc2N1dHRsZSkKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ2NvcnJwbG90KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoRUJJbWFnZSkKbGlicmFyeShnZ3B1YnIpCmBgYApgYGB7cn0KbGlicmFyeShmb3JlYWNoKQpsaWJyYXJ5KGRvUGFyYWxsZWwpCnJlZ2lzdGVyRG9QYXJhbGxlbCg4KQpgYGAKCiMjIyBHZXQgSGlzdG9sb2d5IEltYWdlIGFuZCBQbG90CmBgYHtyfQojIGdldCBoaXN0b2xvZ3kgaW1hZ2UKaW1nIDwtIFNwYXRpYWxFeHBlcmltZW50OjppbWdSYXN0ZXIoc3BlX3N1YikKCiMjIFRyYW5zZm9ybSB0byBhIHJhc3Rlckdyb2Igb2JqZWN0Cmdyb2IgPC0gZ3JpZDo6cmFzdGVyR3JvYihpbWcsIHdpZHRoID0gZ3JpZDo6dW5pdCgxLCAibnBjIiksIGhlaWdodCA9IGdyaWQ6OnVuaXQoMSwgIm5wYyIpKQoKIyMgTWFrZSBhIHBsb3QgdXNpbmcgZ2VvbV9zcGF0aWFsCnAgPC0gZ2dwbG90Mjo6Z2dwbG90KAogICAgc2FtcGxlX2RmLAogICAgZ2dwbG90Mjo6YWVzKAogICAgICAgIHggPSBweGxfY29sX2luX2Z1bGxyZXMgKiBTcGF0aWFsRXhwZXJpbWVudDo6c2NhbGVGYWN0b3JzKHNwZV9zdWIpLAogICAgICAgIHkgPSBweGxfcm93X2luX2Z1bGxyZXMgKiBTcGF0aWFsRXhwZXJpbWVudDo6c2NhbGVGYWN0b3JzKHNwZV9zdWIpLAogICAgKQopICsKICAgIGdlb21fc3BhdGlhbCgKICAgICAgICBkYXRhID0gdGliYmxlOjp0aWJibGUoZ3JvYiA9IGxpc3QoZ3JvYikpLAogICAgICAgIGdncGxvdDI6OmFlcyhncm9iID0gZ3JvYiksCiAgICAgICAgeCA9IDAuNSwKICAgICAgICB5ID0gMC41CiAgICApCgojIyBTaG93IHRoZSBwbG90CnByaW50KHApCmBgYAoKYGBge3J9CnBuZ19saW5rIDwtIGltZ0RhdGEoc3BlKVtpbWdEYXRhKHNwZSkkc2FtcGxlX2lkID09IHNhbXBsZV9pZCxdJGRhdGFbMV0KaW1nIDwtIHJlYWRJbWFnZSgnaHR0cHM6Ly9zcGF0aWFsLWRscGZjLnMzLnVzLWVhc3QtMi5hbWF6b25hd3MuY29tL2ltYWdlcy8xNTE1MDdfdGlzc3VlX2xvd3Jlc19pbWFnZS5wbmcnKQpgYGAKCmBgYHtyfQpyZWQgPC0gaW1hZ2VEYXRhKEVCSW1hZ2U6OmNoYW5uZWwoaW1nLCAncmVkJykpCmdyZWVuIDwtIGltYWdlRGF0YShFQkltYWdlOjpjaGFubmVsKGltZywgJ2dyZWVuJykpCmJsdWUgPC0gaW1hZ2VEYXRhKEVCSW1hZ2U6OmNoYW5uZWwoaW1nLCAnYmx1ZScpKQpncmV5IDwtIGltYWdlRGF0YShFQkltYWdlOjpjaGFubmVsKGltZywgJ2dyZXknKSkKaGlzdChyZWQpCmhpc3QoZ3JlZW4pCmhpc3QoYmx1ZSkKaGlzdChncmV5KQpgYGAKYGBge3J9CmRpc3BsYXkocmVkKQpkaXNwbGF5KGdyZWVuKQpkaXNwbGF5KGJsdWUpCmBgYAoKYGBge3J9CiMgY3JlYXRlIGJvb2xlYW4gYXJyYXkgZm9yIGxvZ2NvdW50cwpsb2djb3VudHNfMTUxNTA3IDwtIGxvZ2NvdW50cyhzcGVfc3ViKQoKIyB0cmFuc3Bvc2UgbG9nY291bnRzIG1hdHJpeApsb2djb3VudHNfMTUxNTA3IDwtIHQobG9nY291bnRzXzE1MTUwNykKCiMgcmVtb3ZlIGNvbHVtbnMgd2l0aCAwIG1heCBnZW5lIGV4cHJlc3Npb24gdmFscyBhY3Jvc3MgYWxsIGJhcmNvZGVzCnp2IDwtIGFwcGx5KGxvZ2NvdW50c18xNTE1MDcsIDIsIGZ1bmN0aW9uKHgpIGxlbmd0aCh1bmlxdWUoeCkpID09IDEpCmxvZ2NvdW50c18xNTE1MDdfZmlsdGVyZWQgPC0gYXMubWF0cml4KGxvZ2NvdW50c18xNTE1MDdbLCF6dl0pCgpkaW0obG9nY291bnRzXzE1MTUwNykKZGltKGxvZ2NvdW50c18xNTE1MDdfZmlsdGVyZWQpCmBgYAoKIyMjIENyZWF0ZSBERiBIb2xkaW5nIFBpeGVsIGxvY2F0aW9ucyBhbmQgUGl4ZWwgQ29sb3IgQ2hhbm5lbCBWYWx1ZXMKYGBge3J9CiMgZ2V0IHNjYWxlIGZhY3RvcgpzY2FsaW5nIDwtIFNwYXRpYWxFeHBlcmltZW50OjpzY2FsZUZhY3RvcnMoc3BlX3N1YikKCiMgZ2V0IG5hbWVzIG9mIGJhcmNvZGVzIGluIHNhbXBsZSwgZmlsdGVyIHB4bCBkYXRhZnJhbWUgdG8gb25seSBpbmNsdWRlIHRob3NlIGJhcmNvZGVzCnNwYXRpYWxfY29vcmRzIDwtIHNwYXRpYWxDb29yZHMoc3BlX3N1YikKCiMgY3JlYXRlIGRhdGEgZnJhbWUKZGF0YSA8LSBkYXRhLmZyYW1lKHNwYXRpYWxfY29vcmRzLCByb3cubmFtZXM9TlVMTCkKZGF0YVsnYmFyY29kZSddIDwtIHJvd25hbWVzKHNwYXRpYWxfY29vcmRzKQoKIyBhZGQgc2NhbGVkIHBpeGVsIGNvbHVtbnMKbmV3IDwtIGNlaWxpbmcoZGF0YVtbInB4bF9jb2xfaW5fZnVsbHJlcyJdXSAqIHNjYWxpbmcpCmRhdGFbICwgbmNvbChkYXRhKSArIDFdIDwtIG5ldyAKY29sbmFtZXMoZGF0YSlbbmNvbChkYXRhKV0gPC0gInNjYWxlZF9weGxfY29sX2luX2xvd3JlcyIKCiMgYWRkIHNjYWxlZCBwaXhlbCByb3dzCm5ldyA8LSBjZWlsaW5nKGRhdGFbWyJweGxfcm93X2luX2Z1bGxyZXMiXV0gKiBzY2FsaW5nKQpkYXRhWyAsIG5jb2woZGF0YSkgKyAxXSA8LSBuZXcgCmNvbG5hbWVzKGRhdGEpW25jb2woZGF0YSldIDwtICJzY2FsZWRfcHhsX3Jvd19pbl9sb3dyZXMiCmBgYAoKYGBge3J9CiMgY3JlYXRlIHZlY3RvcnMgdG8gYmUgYWRkZWQgdG8gZGF0YWZyYW1lClhfciA8LSB2ZWN0b3IobW9kZT0iZG91YmxlIiwgbGVuZ3RoID0gZGltKGRhdGEpWzFdKQpYX2cgPC0gdmVjdG9yKG1vZGU9ImRvdWJsZSIsIGxlbmd0aCA9IGRpbShkYXRhKVsxXSkKWF9iIDwtIHZlY3Rvcihtb2RlPSJkb3VibGUiLCBsZW5ndGggPSBkaW0oZGF0YSlbMV0pClhfZ3JleSA8LSB2ZWN0b3IobW9kZT0iZG91YmxlIiwgbGVuZ3RoID0gZGltKGRhdGEpWzFdKQoKIyBpdGVyYXRlIG92ZXIgZGF0YWZyYW1lLCBhZGQgcmdiIHZhbHVlcwpmb3IgKGkgaW4gMTpucm93KGRhdGEpKSB7CiAgICBjb2xfaW5kZXggPC0gZGF0YVtpLF0kc2NhbGVkX3B4bF9jb2xfaW5fbG93cmVzCiAgICByb3dfaW5kZXggPC0gZGF0YVtpLF0kc2NhbGVkX3B4bF9yb3dfaW5fbG93cmVzCiAgICBYX3JbaV0gPC0gcmVkW2NvbF9pbmRleCwgcm93X2luZGV4XQogICAgWF9nW2ldIDwtIGdyZWVuW2NvbF9pbmRleCwgcm93X2luZGV4XQogICAgWF9iW2ldIDwtIGJsdWVbY29sX2luZGV4LCByb3dfaW5kZXhdCiAgICBYX2dyZXlbaV0gPC0gZ3JleVtjb2xfaW5kZXgsIHJvd19pbmRleF0KfQoKZGF0YVsgLCBuY29sKGRhdGEpICsgMV0gPC0gWF9yCmNvbG5hbWVzKGRhdGEpW25jb2woZGF0YSldIDwtICJyZWQiCmRhdGFbICwgbmNvbChkYXRhKSArIDFdIDwtIFhfZwpjb2xuYW1lcyhkYXRhKVtuY29sKGRhdGEpXSA8LSAiZ3JlZW4iCmRhdGFbICwgbmNvbChkYXRhKSArIDFdIDwtIFhfYgpjb2xuYW1lcyhkYXRhKVtuY29sKGRhdGEpXSA8LSAiYmx1ZSIKZGF0YVsgLCBuY29sKGRhdGEpICsgMV0gPC0gWF9ncmV5CmNvbG5hbWVzKGRhdGEpW25jb2woZGF0YSldIDwtICJncmV5IgpgYGAKCmBgYHtyfQpkaW0oZGF0YSkKZGF0YQpgYGAKCgojIyMgQ3JlYXRlIGluZF9iYXJjb2RlcyBERiBvbmx5IGlmIGVhY2ggc3BvdCBoYXMgbXVsdGlwbGUgcGl4ZWxzIGFzc29jaWF0ZWQgc28gdGhhdCBtZWFuL21lZGlhbi9tb2RlY2FuIGJlIGNvbXB1dGVkIChGb3IgSGlnaFJlcyBJbWFnZXMpCmBgYHtyfQojIE1vZGUgRnVuY3Rpb24KZ2V0bW9kZSA8LSBmdW5jdGlvbih2KSB7CiAgIHVuaXF2IDwtIHVuaXF1ZSh2KQogICB1bmlxdlt3aGljaC5tYXgodGFidWxhdGUobWF0Y2godiwgdW5pcXYpKSldCn0KCiMgCnBvcHVsYXRlX2luZF9iYXJjb2RlcyA8LSBmdW5jdGlvbihpLCBpbmRfYmFyY29kZXMpIHsKICBjb2xvcnMgPC0gYygicmVkIiwgImdyZWVuIiwgImJsdWUiLCAiZ3JleSIpCiAgZm9yIChqIGluIDE6bGVuZ3RoKGNvbG9ycykpIHsKICAgIGNvbG9yIDwtIGNvbG9yc1tqXQogICAgdmFscyA8LSBkYXRhW2RhdGEkYmFyY29kZSA9PSByb3cubmFtZXMoaW5kX2JhcmNvZGVzKVtpXSxdWyxjb2xvcl0KICAgIGluZF9iYXJjb2Rlc1tpLCBwYXN0ZSgibWVhbl8iLCBjb2xvciwgc2VwPSIiKV0gPC0gbWVhbih2YWxzKQogICAgaW5kX2JhcmNvZGVzW2ksIHBhc3RlKCJtZWRpYW5fIiwgY29sb3IsIHNlcD0iIildIDwtIG1lZGlhbih2YWxzKQogICAgaW5kX2JhcmNvZGVzW2ksIHBhc3RlKCJtb2RlXyIsIGNvbG9yLCBzZXA9IiIpXSA8LSBnZXRtb2RlKHZhbHMpIAogIH0KICBpbmRfYmFyY29kZXMKfQpgYGAKCgpgYGB7cn0KIyBjcmVhdGUgZW1wdHkgZGYKaW5kX2JhcmNvZGVzIDwtIGRhdGEuZnJhbWUobWF0cml4KG5jb2wgPSAxMiwgbnJvdyA9IGxlbmd0aCh1bmlxdWUoZGF0YVssJ2JhcmNvZGUnXSkpKSkKY29sbmFtZXMoaW5kX2JhcmNvZGVzKSA8LSBjKCdtZWFuX3JlZCcsICdtZWRpYW5fcmVkJywgJ21vZGVfcmVkJywgJ21lYW5fZ3JlZW4nLCAnbWVkaWFuX2dyZWVuJywgJ21vZGVfZ3JlZW4nLCAnbWVhbl9ibHVlJywgJ21lZGlhbl9ibHVlJywgJ21vZGVfYmx1ZScsICdtZWFuX2dyZXknLCAnbWVkaWFuX2dyZXknLCAnbW9kZV9ncmV5JykKcm93bmFtZXMoaW5kX2JhcmNvZGVzKSA8LSB1bmlxdWUoZGF0YVssICdiYXJjb2RlJ10pCgojIGFkZCBtZWFuLCBtZWRpYW4sIG1vZGUgZGF0YQpmb3IgKGkgaW4gMTpucm93KGluZF9iYXJjb2RlcykpIHsKICBpbmRfYmFyY29kZXMgPC0gcG9wdWxhdGVfaW5kX2JhcmNvZGVzKGksIGluZF9iYXJjb2RlcykKfQppbmRfYmFyY29kZXMKYGBgCgojIyMgUGxvdCBIaXN0b2dyYW0gb2YgQ29sb3IgQ2hhbm5lbCBWYWx1ZXMgYXQgYSBnaXZlbiBiYXJjb2RlCmBgYHtyfQojIGhpc3RvZ3JhbSBvZiBjb2xvciBjaGFubmVsIHZhbHVlcyBpbiBvbmUgc3BvdApiYXJjb2RlID0gcm93bmFtZXMoaW5kX2JhcmNvZGVzKVsxXQp4IDwtIGRhdGFbZGF0YSRiYXJjb2RlID09IGJhcmNvZGUsJ2dyZWVuJ10KaGlzdCh4LCBtYWluPSJIaXN0b2dyYW0gb2YgR3JlZW4gQ29sb3IgQ2hhbm5lbCBWYWx1ZXMgRm9yIGEgU2luZ2xlIEJhcmNvZGUiLGJyZWFrcz0xMDApCmBgYAoKIyMjIEZ1bmN0aW9uIHRvIENvbXB1dGUgQ29yciBNYXRyaXggaW4gUGFyYWxsZWwKYGBge3J9CmNvbXB1dGVfY29ycl9tYXRyaXggPC0gZnVuY3Rpb24oZXhwcmVzc2lvbl9hcnIsIGNvbG9yX2FycikgewogIGZvcmVhY2goaSA9IDE6bmNvbChleHByZXNzaW9uX2FyciksCiAgLmNvbWJpbmUgPSByYmluZCwKICAucGFja2FnZXMgPSBjKCdkYXRhLnRhYmxlJywgJ2RvUGFyYWxsZWwnKSkgJWRvcGFyJSB7CiAgICBjb2xOYW1lIDwtIGNvbG5hbWVzKGV4cHJlc3Npb25fYXJyKVtpXQogICAgZGYgPC0gZGF0YS5mcmFtZShyb3VuZChjb3IoZXhwcmVzc2lvbl9hcnJbLGldLCBjb2xvcl9hcnIsIG1ldGhvZCA9ICdwZWFyc29uJywgdXNlPSJjb21wbGV0ZS5vYnMiKSwgMykpCiAgICByb3duYW1lcyhkZikgPC0gY29sTmFtZQogICAgZGYKICB9Cn0KYGBgCgojIyMgQW5hbHlzaXMgMTogQ29tcHV0ZSBDb3JyZWxhdGlvbiBGaXJzdCwgQ29tcHV0ZSBPdXRsaWVycyBBZnRlcgpJbiB0aGUgZm9sbG93aW5nIGJsb2NrcywgdGhlIGNvcnJlbGF0aW9uIG9mIGJldHdlZW4gYWxsIGdlbmVzICsgcGl4ZWwgY29sb3IgY2hhbm5lbCB2YWx1ZXMgaXMgY2FsY3VsYXRlZC4gVGhlbiwgZm9yIHRoZSB0b3AgcGVyZm9ybWluZyBnZW5lcywgYSBwbG90dGluZyBmdW5jdGlvbiBpcyBjYWxsZWQgaW4gd2hpY2ggc3BvdHMgY29ycmVzcG9uZGluZyB0byBvdXRsaWVyIGdlbmUgZXhwcmVzc2lvbiB2YWx1ZXMgKyBzcG90cyBjb3JyZXNwb25kaW5nIHRvIG91dGxpZXIgcGl4ZWwgY29sb3IgY2hhbm5lbCB2YWx1ZXMgYXJlIHJlbW92ZWQuCgpgYGB7cn0KIyB1bmNvbW1lbnQgdGhpcyBsaW5lIGlmIGVhY2ggc3BvdCBoYXMgb25lIHBpeGVsCmNvbG9yX2FyciA8LSBkYXRhWyw2Om5jb2woZGF0YSldCgojIHVuY29tbWVudCB0aGlzIGxpbmUgaWYgZWFjaCBzcG90IGhhcyBtdWx0aXBsZSBwaXhlbHMKIyBjb2xvcl9hcnIgPC0gaW5kX2JhcmNvZGVzCmNvcnJlbGF0aW9uX21hdHJpeCA8LSBjb21wdXRlX2NvcnJfbWF0cml4KGxvZ2NvdW50c18xNTE1MDdfZmlsdGVyZWQsIGNvbG9yX2FycikKYGBgCgpgYGB7cn0KIyBzZXQgdGhyZXNob2xkIGFuZCBmaWx0ZXIKdGhyZXNob2xkIDwtIDAuMwpjb3JyZWxhdGlvbl9tYXRyaXhfZmlsdGVyIDwtIGNvcnJlbGF0aW9uX21hdHJpeApmaWx0ZXIgPC0gYXMuZGF0YS5mcmFtZShhcHBseShhYnMoY29ycmVsYXRpb25fbWF0cml4X2ZpbHRlcikgPj0gdGhyZXNob2xkLCAxLCBhbnkpKQpjb3JyZWxhdGlvbl9tYXRyaXhfZmlsdGVyIDwtIGNvcnJlbGF0aW9uX21hdHJpeF9maWx0ZXJbZmlsdGVyWywxXSxdCgojIHJlbmFtZSByb3duYW1lcyBiYXNlZCBvbiBnZW5lIG5hbWUgaW5zdGVhZCBvZiBnZW5lIElECmZvciAoaSBpbiAxOm5yb3coY29ycmVsYXRpb25fbWF0cml4X2ZpbHRlcikpIHsKICBnX25hbWUgPC0gcm93bmFtZXMoY29ycmVsYXRpb25fbWF0cml4X2ZpbHRlcilbaV0KICByb3duYW1lcyhjb3JyZWxhdGlvbl9tYXRyaXhfZmlsdGVyKVtpXSA8LSByb3dEYXRhKHNwZVtnX25hbWVdKSRnZW5lX25hbWUKfQpjb3JyZWxhdGlvbl9tYXRyaXhfZmlsdGVyCmBgYAoKYGBge3J9CiMgcGxvdCAgY29ycmVsYXRpb24gbWF0cml4CmdnY29ycnBsb3QoY29ycmVsYXRpb25fbWF0cml4X2ZpbHRlciwgc2lnLmxldmVsPTAuMDEsIGxhYl9zaXplID0gNC41LCBwLm1hdCA9IE5VTEwsCiAgICAgICAgICAgaW5zaWcgPSBjKCJwY2giLCAiYmxhbmsiKSwgcGNoID0gMSwgcGNoLmNvbCA9ICJibGFjayIsIHBjaC5jZXggPTEsCiAgICAgICAgICAgdGwuY2V4ID0gNykgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KG1hcmdpbj1tYXJnaW4oLTIsMCwwLDApKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChtYXJnaW49bWFyZ2luKDAsLTIsMCwwKSksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfbGluZShzaXplPTcpKSArIAogIGdlb21fdGlsZShmaWxsPSJ3aGl0ZSIpICsKICBnZW9tX3RpbGUoaGVpZ2h0PTEsIHdpZHRoPTEpCmBgYAoKCmBgYHtyfQojIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSByZWdyZXNzaW9uIGVxdWF0aW9uLiBVc2VkIGFzIGEgc2FuaXR5IGNoZWNrCmxtX2VxbiA8LSBmdW5jdGlvbihkZil7CiAgICBtIDwtIGxtKG1lYW5fcmVkIH4gTVRSTlIyTDgsIGRmKTsKICAgIGVxIDwtIHN1YnN0aXR1dGUoaXRhbGljKHkpID09IGEgKyBiICUuJSBpdGFsaWMoeCkqIiwifn5pdGFsaWMocileMn4iPSJ+cjIsIAogICAgICAgICBsaXN0KGEgPSBmb3JtYXQodW5uYW1lKGNvZWYobSlbMV0pLCBkaWdpdHMgPSAyKSwKICAgICAgICAgICAgICBiID0gZm9ybWF0KHVubmFtZShjb2VmKG0pWzJdKSwgZGlnaXRzID0gMiksCiAgICAgICAgICAgICByMiA9IGZvcm1hdChzdW1tYXJ5KG0pJHIuc3F1YXJlZCwgZGlnaXRzID0gMykpKQogICAgYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKTsKfQoKIyBwbG90IGNvbG9yIGNoYW5uZWwgdmFsdWUgdnMgZ2VuZSBleHByZXNzaW9uLiBJZiByZW1vdmVfb3V0bGllcnMgPT0gVFJVRSwgb3V0bGllciBnZW5lIGV4cHJlc3Npb24gdmFsdWVzICsgb3V0bGllciBwaXhlbCBjb2xvciB2YWx1ZXMgYXJlIHJlbW92ZWQuCiMgQVNTVU1FUyBUSEFUIEVBQ0ggQkFSQ09ERSBIQVMgTVVMVElQTEUgUElYRUxTCnBsb3RfY2hhbm5lbF9oaWdocmVzIDwtIGZ1bmN0aW9uKGdfbmFtZSwgZ2VuZV9pZCwgY29sb3IsIHN0YXQsIGxvZ19hcnIsIGNvbG9yX2FyciwgcmVtb3ZlX291dGxpZXJzKSB7CiAgc3RhdF90eXBlIDwtIHBhc3RlKGNvbG9yLCBzdGF0LCBzZXA9Il8iKQogIHhfYXhpcyA8LSBsb2dfYXJyWyxnZW5lX2lkXQogIHlfYXhpcyA8LSBjb2xvcl9hcnJbLHN0YXRfdHlwZV0gKiAyNTUKICBkZiA8LSBkYXRhLmZyYW1lKHhfYXhpcywgeV9heGlzKQogIGlmIChyZW1vdmVfb3V0bGllcnMpIHsKICAgIGJhcmNvZGVfbmFtZXMgPC0gcm93bmFtZXMobG9nX2FycikKICAgIG92ZXJsYXAgPC0gdW5pb24ocm93bmFtZXMobG9nX2FycilbaXNPdXRsaWVyKGxvZ19hcnJbLGdlbmVfaWRdKV0sIHJvd25hbWVzKGNvbG9yX2FycilbaXNPdXRsaWVyKGNvbG9yX2Fyclssc3RhdF90eXBlXSldKQogICAgcm93bmFtZXMoZGYpIDwtIGJhcmNvZGVfbmFtZXMKICAgIGRmIDwtIGRmWy13aGljaChyb3duYW1lcyhkZikgJWluJSBvdmVybGFwKSxdCiAgICB0aXRsZSA8LSBwYXN0ZShzdGF0X3R5cGUsICIgQ2hhbm5lbCBWYWx1ZXMgdnMgIiwgZ19uYW1lLCAiIEdlbmUgRXhwcmVzc2lvbiB3aXRoIE91dGxpZXJzIFJlbW92ZWQiLCBzZXA9IiIpCiAgfSBlbHNlIHsKICAgIHRpdGxlIDwtIHBhc3RlKHN0YXRfdHlwZSwgIiBDaGFubmVsIFZhbHVlcyB2cyAiLCBnX25hbWUsICIgR2VuZSBFeHByZXNzaW9uIHdpdGhvdXQgT3V0bGllcnMgUmVtb3ZlZCIsIHNlcD0iIikKICB9CiAgZ2dwbG90KGRmLCBhZXMoeD1kZlssMV0sIHk9ZGZbLDJdKSkgKyB4bGFiKGdfbmFtZSkgKyB5bGFiKHN0YXRfdHlwZSkgKyBnZW9tX3BvaW50KGNvbG9yPWNvbG9yKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScsIGNvbG9yPSdibGFjaycpICsgc3RhdF9yZWdsaW5lX2VxdWF0aW9uKGxhYmVsLnkgPSAxOTAsIGFlcyhsYWJlbCA9IC4uZXEubGFiZWwuLikpICsKICBzdGF0X3JlZ2xpbmVfZXF1YXRpb24obGFiZWwueSA9IDE4MCwgYWVzKGxhYmVsID0gLi5yci5sYWJlbC4uKSkgKyBnZ3RpdGxlKHRpdGxlKSAKfQoKIyBjYWxsIHBsb3RfY2hhbm5lbCBmdW5jdGlvbiBmb3IgYWxsIGNvbG9yIGFuZCBtZWFuL21lZGlhbi9tb2RlIGNvbWJpbmF0aW9ucwojIEFTU1VNRVMgVEhBVCBFQUNIIEJBUkNPREUgSEFTIE1VTFRJUExFIFBJWEVMUwpwbG90X2FsbF9jaGFubmVsc19oaWdocmVzIDwtIGZ1bmN0aW9uKGdfbmFtZSwgbG9nX2FyciwgY29sb3JfYXJyLCByZW1vdmVfb3V0bGllcnMpIHsKICBnZW5lX2lkIDwtIHJvd0RhdGEoc3BlKVtyb3dEYXRhKHNwZSkkZ2VuZV9uYW1lID09IGdfbmFtZSxdJGdlbmVfaWQKICBjb2xvcnMgPC0gYygncmVkJywgJ2dyZWVuJywgJ2JsdWUnLCAnZ3JleScpCiAgc3RhdHMgPC0gYygnbWVhbicsICdtZWRpYW4nLCAnbW9kZScpCgogIGZvciAoY29sb3IgaW4gY29sb3JzKSB7CiAgICBmb3IgKHN0YXQgaW4gc3RhdHMpIHsKICAgICAgcHJpbnQocGxvdF9jaGFubmVsX2hpZ2hyZXMoZ19uYW1lLCBnZW5lX2lkLCBjb2xvciwgc3RhdCwgbG9nX2FyciwgY29sb3JfYXJyLCByZW1vdmVfb3V0bGllcnMpKSAKICAgIH0KICB9Cn0KCiMgcGxvdCBjb2xvciBjaGFubmVsIHZhbHVlIHZzIGdlbmUgZXhwcmVzc2lvbi4gSWYgcmVtb3ZlX291dGxpZXJzID09IFRSVUUsIG91dGxpZXIgZ2VuZSBleHByZXNzaW9uIHZhbHVlcyArIG91dGxpZXIgcGl4ZWwgY29sb3IgdmFsdWVzIGFyZSByZW1vdmVkLgojIEFTU1VNRVMgVEhBVCBFQUNIIEJBUkNPREUgSEFTIE9ORSBQSVhFTApwbG90X2NoYW5uZWwgPC0gZnVuY3Rpb24oZ19uYW1lLCBnZW5lX2lkLCBjb2xvciwgbG9nX2FyciwgY29sb3JfYXJyLCByZW1vdmVfb3V0bGllcnMpIHsKICB4X2F4aXMgPC0gbG9nX2FyclssZ2VuZV9pZF0KICB5X2F4aXMgPC0gY29sb3JfYXJyWyxjb2xvcl0gKiAyNTUKICBkZiA8LSBkYXRhLmZyYW1lKHhfYXhpcywgeV9heGlzKQogIGlmIChyZW1vdmVfb3V0bGllcnMpIHsKICAgIGJhcmNvZGVfbmFtZXMgPC0gcm93bmFtZXMobG9nX2FycikKICAgIG92ZXJsYXAgPC0gdW5pb24ocm93bmFtZXMobG9nX2FycilbaXNPdXRsaWVyKGxvZ19hcnJbLGdlbmVfaWRdKV0sIHJvd25hbWVzKGNvbG9yX2FycilbaXNPdXRsaWVyKGNvbG9yX2FyclssY29sb3JdKV0pCiAgICByb3duYW1lcyhkZikgPC0gYmFyY29kZV9uYW1lcwogICAgZGYgPC0gZGZbLXdoaWNoKHJvd25hbWVzKGRmKSAlaW4lIG92ZXJsYXApLF0KICAgIHRpdGxlIDwtIHBhc3RlKGNvbG9yLCAiIENoYW5uZWwgVmFsdWVzIHZzICIsIGdfbmFtZSwgIiBHZW5lIEV4cHJlc3Npb24gd2l0aCBPdXRsaWVycyBSZW1vdmVkIiwgc2VwPSIiKQogIH0gZWxzZSB7CiAgICB0aXRsZSA8LSBwYXN0ZShjb2xvciwgIiBDaGFubmVsIFZhbHVlcyB2cyAiLCBnX25hbWUsICIgR2VuZSBFeHByZXNzaW9uIHdpdGhvdXQgT3V0bGllcnMgUmVtb3ZlZCIsIHNlcD0iIikKICB9CiAgZ2dwbG90KGRmLCBhZXMoeD1kZlssMV0sIHk9ZGZbLDJdKSkgKyB4bGFiKGdfbmFtZSkgKyB5bGFiKGNvbG9yKSArIGdlb21fcG9pbnQoY29sb3I9Y29sb3IpICsKICBnZW9tX3Ntb290aChtZXRob2Q9J2xtJywgY29sb3I9J2JsYWNrJykgKyBzdGF0X3JlZ2xpbmVfZXF1YXRpb24obGFiZWwueSA9IDE5MCwgYWVzKGxhYmVsID0gLi5lcS5sYWJlbC4uKSkgKwogIHN0YXRfcmVnbGluZV9lcXVhdGlvbihsYWJlbC55ID0gMTgwLCBhZXMobGFiZWwgPSAuLnJyLmxhYmVsLi4pKSArIGdndGl0bGUodGl0bGUpIAp9CgojIGNhbGwgcGxvdF9jaGFubmVsIGZ1bmN0aW9uIGZvciBhbGwgY29sb3JzCiMgQVNTVU1FUyBUSEFUIEVBQ0ggQkFSQ09ERSBIQVMgT05FIFBJWEVMCnBsb3RfYWxsX2NoYW5uZWxzIDwtIGZ1bmN0aW9uKGdfbmFtZSwgbG9nX2FyciwgY29sb3JfYXJyLCByZW1vdmVfb3V0bGllcnMpIHsKICBnZW5lX2lkIDwtIHJvd0RhdGEoc3BlKVtyb3dEYXRhKHNwZSkkZ2VuZV9uYW1lID09IGdfbmFtZSxdJGdlbmVfaWQKICBjb2xvcnMgPC0gYygncmVkJywgJ2dyZWVuJywgJ2JsdWUnLCAnZ3JleScpCgogIGZvciAoY29sb3IgaW4gY29sb3JzKSB7CiAgICBwcmludChwbG90X2NoYW5uZWwoZ19uYW1lLCBnZW5lX2lkLCBjb2xvciwgbG9nX2FyciwgY29sb3JfYXJyLCByZW1vdmVfb3V0bGllcnMpKQogIH0KfQpgYGAKCmBgYHtyfQpwbG90X2FsbF9jaGFubmVscygnTVQtTkQxJywgbG9nY291bnRzXzE1MTUwN19maWx0ZXJlZCwgY29sb3JfYXJyLCByZW1vdmVfb3V0bGllcnM9RkFMU0UpCnBsb3RfYWxsX2NoYW5uZWxzKCdNVC1ORDEnLCBsb2djb3VudHNfMTUxNTA3X2ZpbHRlcmVkLCBjb2xvcl9hcnIsIHJlbW92ZV9vdXRsaWVycz1UUlVFKQpgYGAKCmBgYHtyfQpwbG90X2FsbF9jaGFubmVscygnTVQtQ08yJywgbG9nY291bnRzXzE1MTUwN19maWx0ZXJlZCwgY29sb3JfYXJyLCByZW1vdmVfb3V0bGllcnM9RkFMU0UpCnBsb3RfYWxsX2NoYW5uZWxzKCdNVC1DTzInLCBsb2djb3VudHNfMTUxNTA3X2ZpbHRlcmVkLCBjb2xvcl9hcnIsIHJlbW92ZV9vdXRsaWVycz1UUlVFKQpgYGAKYGBge3J9CnBsb3RfYWxsX2NoYW5uZWxzKCdNVC1BVFA2JywgbG9nY291bnRzXzE1MTUwN19maWx0ZXJlZCwgY29sb3JfYXJyLCByZW1vdmVfb3V0bGllcnM9RkFMU0UpCnBsb3RfYWxsX2NoYW5uZWxzKCdNVC1BVFA2JywgbG9nY291bnRzXzE1MTUwN19maWx0ZXJlZCwgY29sb3JfYXJyLCByZW1vdmVfb3V0bGllcnM9VFJVRSkKYGBgCmBgYHtyfQpwbG90X2FsbF9jaGFubmVscygnTVQtQ1lCJywgbG9nY291bnRzXzE1MTUwN19maWx0ZXJlZCwgY29sb3JfYXJyLCByZW1vdmVfb3V0bGllcnM9RkFMU0UpCnBsb3RfYWxsX2NoYW5uZWxzKCdNVC1DWUInLCBsb2djb3VudHNfMTUxNTA3X2ZpbHRlcmVkLCBjb2xvcl9hcnIsIHJlbW92ZV9vdXRsaWVycz1UUlVFKQpgYGAKCgojIyBSZWRvIEFuYWx5c2lzIFJlbW92aW5nIE91dGxpZXJzIEJlZm9yZSBDb3JyZWxhdGlvbiBDb21wdXRhdGlvbgoKYGBge3J9CiMgUmVtb3ZlIG91dGxpZXIgZ2VuZSBleHByZXNzaW9uIHZhbHVlcy4gSWYgbWVkaWFuIGdlbmUgZXhwcmVzc2lvbiB2YWx1ZSBpcyAwLCByZXBsYWNlIGNvbXBsZW1lbnQgb2Ygb3V0bGllcnMgd2l0aCBOQS4gRWxzZSByZXBsYWNlIG91dGxpZXJzIHdpdGggTkEuIElmIHJlbWFpbmluZyBub24tTkEgZXhwcmVzc2lvbiB2YWx1ZXMgPCBudW0sIHJlcGxhY2UgZW50aXJlIGNvbHVtbiB3aXRoIE5BLgpyZW1vdmVfb3V0bGllcnMgPC0gZnVuY3Rpb24oYXJyLCBudW0pIHsKICBmb3JlYWNoKGkgPSAxOm5jb2woYXJyKSwKICAuY29tYmluZSA9IGNiaW5kLAogIC5wYWNrYWdlcyA9IGMoJ2RhdGEudGFibGUnLCAnZG9QYXJhbGxlbCcpKSAlZG9wYXIlIHsKICAgIGlmIChtZWRpYW4oYXJyWyxpXSkgPT0gMCkgewogICAgICBhcnJbLGldWyFpc091dGxpZXIoYXJyWyxpXSldIDwtIE5BCiAgICB9IGVsc2UgewogICAgICBhcnJbLGldW2lzT3V0bGllcihhcnJbLGldKV0gPC0gTkEKICAgIH0KICAgIGlmIChtYXRyaXhTdGF0czo6Y291bnQoIWlzLm5hKGFyclssaV0pKSA8IG51bSkgewogICAgICBhcnJbLGldIDwtIHJlcChOQSwgbnJvdyhhcnIpKQogICAgfQogICAgYXJyWyxpXQogIH0KfQoKYGBgCgpgYGB7cn0KIyBDYWxsIHJlbW92ZSBvdXRsaWVyIGZ1bmN0aW9uCmxvZ2NvdW50c190ZW1wIDwtIGFzLm1hdHJpeChsb2djb3VudHNfMTUxNTA3X2ZpbHRlcmVkKQpjb2xfbmFtZXMgPC0gY29sbmFtZXMobG9nY291bnRzX3RlbXApCmxvZ2NvdW50c18xNTE1MDdfZmlsdGVyZWRfbm9fb3V0bGllcnMgPC0gcmVtb3ZlX291dGxpZXJzKGxvZ2NvdW50c190ZW1wLCAxMDApCmNvbG5hbWVzKGxvZ2NvdW50c18xNTE1MDdfZmlsdGVyZWRfbm9fb3V0bGllcnMpIDwtIGNvbF9uYW1lcwpsb2djb3VudHNfMTUxNTA3X2ZpbHRlcmVkX25vX291dGxpZXJzCmBgYAoKYGBge3J9CiMgUmVtb3ZlIGNvbHVtbnMgdGhhdCBhcmUgY29tcGxldGVseSBOQQpub3RfYWxsX25hIDwtIGZ1bmN0aW9uKHgpIGFueSghaXMubmEoeCkpCmxvZ2NvdW50c18xNTE1MDdfZmlsdGVyZWRfbm9fb3V0bGllcnMgPC0gZGF0YS5mcmFtZShsb2djb3VudHNfMTUxNTA3X2ZpbHRlcmVkX25vX291dGxpZXJzKSAlPiUgc2VsZWN0KHdoZXJlKG5vdF9hbGxfbmEpKQpgYGAKCmBgYHtyfQpkaW0obG9nY291bnRzXzE1MTUwN19maWx0ZXJlZF9ub19vdXRsaWVycykKYGBgCgpgYGB7cn0KIyBjb21wdXRlIGNvcnJlbGF0aW9uIG1hdHJpeAptYXQgPC0gY29tcHV0ZV9jb3JyX21hdHJpeChsb2djb3VudHNfMTUxNTA3X2ZpbHRlcmVkX25vX291dGxpZXJzLCBjb2xvcl9hcnIpCmBgYAoKCmBgYHtyfQojIGZpbHRlciBjb3JyIG1hdHJpeCBiYXNlZCBvbiB0aHJlc2hvbGQKdGhyZXNob2xkIDwtIDAuMzUKY29ycmVsYXRpb25fbWF0cml4X2ZpbHRlciA8LSBtYXQKZmlsdGVyIDwtIGFzLmRhdGEuZnJhbWUoYXBwbHkoYWJzKGNvcnJlbGF0aW9uX21hdHJpeF9maWx0ZXIpID49IHRocmVzaG9sZCwgMSwgYW55KSkKY29ycmVsYXRpb25fbWF0cml4X2ZpbHRlciA8LSBuYS5vbWl0KGNvcnJlbGF0aW9uX21hdHJpeF9maWx0ZXJbZmlsdGVyWywxXSxdKQpjb3JyZWxhdGlvbl9tYXRyaXhfZmlsdGVyCmBgYAoKYGBge3J9CiMgcmVuYW1lIHJvd3Mgb2YgY29ycmVsYXRpb24gbWF0cml4IHdpdGggZ2VuZSBuYW1lCmZvciAoaSBpbiAxOm5yb3coY29ycmVsYXRpb25fbWF0cml4X2ZpbHRlcikpIHsKICBnX25hbWUgPC0gcm93bmFtZXMoY29ycmVsYXRpb25fbWF0cml4X2ZpbHRlcilbaV0KICBwcmludChnX25hbWUpCiAgcm93bmFtZXMoY29ycmVsYXRpb25fbWF0cml4X2ZpbHRlcilbaV0gPC0gcm93RGF0YShzcGVbZ19uYW1lXSkkZ2VuZV9uYW1lCn0KY29ycmVsYXRpb25fbWF0cml4X2ZpbHRlcgpgYGAKCmBgYHtyfQojIENvcnJlbGF0aW9uIHBsb3QgdmlzdWFsaXphdGlvbgpnZ2NvcnJwbG90KGNvcnJlbGF0aW9uX21hdHJpeF9maWx0ZXIsIHNpZy5sZXZlbD0wLjAxLCBsYWJfc2l6ZSA9IDQuNSwgcC5tYXQgPSBOVUxMLAogICAgICAgICAgIGluc2lnID0gYygicGNoIiwgImJsYW5rIiksIHBjaCA9IDEsIHBjaC5jb2wgPSAiYmxhY2siLCBwY2guY2V4ID0xLAogICAgICAgICAgIHRsLmNleCA9IDcpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChtYXJnaW49bWFyZ2luKC0yLDAsMCwwKSksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQobWFyZ2luPW1hcmdpbigwLC0yLDAsMCkpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2xpbmUoc2l6ZT03KSkgKyAKICBnZW9tX3RpbGUoZmlsbD0id2hpdGUiKSArCiAgZ2VvbV90aWxlKGhlaWdodD0xLCB3aWR0aD0xKQpgYGAKYGBge3J9CnBsb3RfYWxsX2NoYW5uZWxzKCdNQVJDMScsIGxvZ2NvdW50c18xNTE1MDdfZmlsdGVyZWRfbm9fb3V0bGllcnMsIGNvbG9yX2FyciwgcmVtb3ZlX291dGxpZXJzPUZBTFNFKQpgYGAKCmBgYHtyfQpwbG90X2FsbF9jaGFubmVscygnSFNEMTdCNicsIGxvZ2NvdW50c18xNTE1MDdfZmlsdGVyZWRfbm9fb3V0bGllcnMsIGNvbG9yX2FyciwgcmVtb3ZlX291dGxpZXJzPUZBTFNFKQpgYGAKCmBgYHtyfQpwbG90X2FsbF9jaGFubmVscygnTVNYMScsIGxvZ2NvdW50c18xNTE1MDdfZmlsdGVyZWRfbm9fb3V0bGllcnMsIGNvbG9yX2FyciwgcmVtb3ZlX291dGxpZXJzPUZBTFNFKQpgYGAKCmBgYHtyfQpwbG90X2FsbF9jaGFubmVscygnTVQtQ08yJywgbG9nY291bnRzXzE1MTUwN19maWx0ZXJlZF9ub19vdXRsaWVycywgY29sb3JfYXJyLCByZW1vdmVfb3V0bGllcnM9RkFMU0UpCmBgYAoKYGBge3J9CnBsb3RfYWxsX2NoYW5uZWxzKCdQTElONCcsIGxvZ2NvdW50c18xNTE1MDdfZmlsdGVyZWRfbm9fb3V0bGllcnMsIGNvbG9yX2FyciwgcmVtb3ZlX291dGxpZXJzPUZBTFNFKQpgYGAKCmBgYHtyfQpwbG90X2FsbF9jaGFubmVscygnQ0lERUMnLCBsb2djb3VudHNfMTUxNTA3X2ZpbHRlcmVkX25vX291dGxpZXJzLCBjb2xvcl9hcnIsIHJlbW92ZV9vdXRsaWVycz1GQUxTRSkKYGBgCgpgYGB7cn0KcGxvdF9hbGxfY2hhbm5lbHMoJ0VMSzQnLCBsb2djb3VudHNfMTUxNTA3X2ZpbHRlcmVkX25vX291dGxpZXJzLCBjb2xvcl9hcnIsIHJlbW92ZV9vdXRsaWVycz1GQUxTRSkKYGBgCgoKYGBge3J9CnBsb3RfYWxsX2NoYW5uZWxzKCdMRk5HJywgbG9nY291bnRzXzE1MTUwN19maWx0ZXJlZF9ub19vdXRsaWVycywgY29sb3JfYXJyLCByZW1vdmVfb3V0bGllcnM9RkFMU0UpCmBgYApgYGB7cn0KZGltKGxvZ2NvdW50c190ZW1wKQpkaW0obG9nY291bnRzXzE1MTUwN19maWx0ZXJlZF9ub19vdXRsaWVycykKYGBgCgoKIyMjIEZpbmQgTG9jYXRpb24gb2YgT3V0bGllcnMgb24gSGlzdG9sb2d5IEltYWdlCmBgYHtyfQojIHBsb3Rfb3V0bGllcnNfaW5fbG93cmVzIDwtIGZ1bmN0aW9uKGdfbmFtZSwgc3RhdF90eXBlKSB7CiMgICBvdmVybGFwIDwtIHVuaW9uKHJvd25hbWVzKG1heF9leF9jb3JyKVtpc091dGxpZXIobWF4X2V4X2NvcnJbLGdfbmFtZV0pXSwgcm93bmFtZXMoaW5kX2JhcmNvZGVzKVtpc091dGxpZXIoaW5kX2JhcmNvZGVzWyxzdGF0X3R5cGVdKV0pCiMgICB4X2F4aXMgPC0gZGF0YVt3aGljaChkYXRhJGJhcmNvZGUgJWluJSBvdmVybGFwKSxdJHNjYWxlZF9weGxfY29sX2luX2xvd3JlcwojICAgeV9heGlzIDwtIGRhdGFbd2hpY2goZGF0YSRiYXJjb2RlICVpbiUgb3ZlcmxhcCksXSRzY2FsZWRfcHhsX3Jvd19pbl9sb3dyZXMKIyAgIAojICAgZ2dwbG90KGRhdGEuZnJhbWUoeF9heGlzLCB5X2F4aXMpLCBhZXMoeD14X2F4aXMsIHk9eV9heGlzKSkgKyB4bGFiKCdTY2FsZWQgUGl4ZWwgQ29sdW1uIGluIExvd1JlcycpICsgeWxhYignU2NhbGVkIFBpeGVsIFJvdyBpbiBMb3dSZXMnKSArIGdlb21fcG9pbnQoY29sb3I9J25hdnknKSArCiMgICAgIGdndGl0bGUocGFzdGUoJ0xvY2F0aW9uIG9mJyxzdGF0X3R5cGUsIGdfbmFtZSwgIk91dGxpZXJzIG9uIExvd1JlcyBJbWFnZSIsIHNlcD0iICIpKSAKIyB9CiMgYGBgCiMgCiMgCiMgYGBge3J9CiMgcGxvdF9vdXRsaWVyc19pbl9sb3dyZXMoJ01UMycsICdtZWFuX3JlZCcpCiMgcGxvdF9vdXRsaWVyc19pbl9sb3dyZXMoJ01UMycsICdtZWFuX2JsdWUnKQojIHBsb3Rfb3V0bGllcnNfaW5fbG93cmVzKCdNVDMnLCAnbWVhbl9ncmVlbicpCiMgYGBgCiMgCiMgYGBge3J9CiMgcGxvdF9vdXRsaWVyc19pbl9sb3dyZXMoJ01UUk5SMkw4JywgJ21lYW5fcmVkJykKIyBwbG90X291dGxpZXJzX2luX2xvd3JlcygnTVRSTlIyTDgnLCAnbWVhbl9ibHVlJykKIyBwbG90X291dGxpZXJzX2luX2xvd3JlcygnTVRSTlIyTDgnLCAnbWVhbl9ncmVlbicpCiMgYGBgCgo=